/* 
 * File:   EntityManager.cpp
 * Author: RedEyedKiller
 * 
 * Created on 24 Απρίλιος 2010, 12:28 πμ
 */

#include "EntityManager.h"
#include "Entity.h"
#include "RenderingManager.h"
#include "KeyMapper.h"
#include "Logger.h"
#include "ServiceLocator.h"
#include "KeyMapper.h"
#include "KeyboardEvent.h"

#include <vector>
#include <algorithm>

namespace EntitySystem
{

/*******************************************************************************
 *Here goes a bunch of functors and predicates used  by the Entity Manager.      *
 *******************************************************************************/

/**
 * Predicate that returns if an Entity is dead.
 */

struct EntityDead : public unary_function< Entity*, bool >
{

    bool operator( )( Entity * entity ) const
    {
        return !entity->IsAlive( );
    }
};

/**
 * Functor that updates an entity. 
 *
 * @param entity - entity to be updated 
 */

struct EntityUpdate
{

    void operator( )( Entity * entity )
    {
        entity->Update( );
    }
};

/**
 * Compares an Entity's ID to an ID passed as a parameter.
 */

class CompareID
{
public:

    CompareID(const std::string &id)
    :
    m_id(id)
    {
    }

    bool operator( )( Entity* entity )
    {
        return entity->GetID( ) == m_id;
    }

private:
    const std::string m_id;
};

/******************************************************************************/


EntityManager::EntityManager()
{
    creator = new EntityCreator;
}

EntityManager::~EntityManager()
{
    delete creator;

    std::for_each( entityPool.begin( ), entityPool.end( ), FreePointer< Entity > ( ) );
}

Entity* EntityManager::FindEntity(const std::string &ID)
{
    std::list< Entity* >::iterator entity = std::find_if( entityPool.begin( ), entityPool.end( ), CompareID( ID ) );

    if( entity != entityPool.end( ) )
    {
        return *entity;
    }

    return NULL;
}

void EntityManager::AddEntity(Entity* entity)
{
    entityPool.push_back( entity );
}

Entity* EntityManager::RequestEntityImidAt(const std::string &subtype, int x, int y)
{
    Entity* entity = creator->CreateEntity( subtype );
    if( entity )
    {
        entity->SetPosition( static_cast < float > ( x ), static_cast < float > ( y ) );
        AddEntity( entity );
    }
    return entity;
}

Entity* EntityManager::RequestEntityImidAt(const std::string &subtype, const Math::Vector2I& position)
{
    Entity* entity = creator->CreateEntity( subtype );
    if( entity )
    {
        entity->SetPosition( position.ConvertTo<float>( ) );
        AddEntity( entity );
    }
    return entity;
}

bool EntityManager::CheckPrototype(const std::string &subtype)
{
    return creator->CheckPrototype( subtype );
}

bool EntityManager::RegisterEntityPrototype(Entity* prototype, const std::string &subtype)
{
    return creator->RegisterEntityPrototype( prototype, subtype );
}

void EntityManager::UpdateEntities()
{
    std::for_each( entityPool.begin( ), entityPool.end( ), EntityUpdate( ) );
}

/**
 * Frees memory allocated to dead entities.
 *
 * @param state -
 */

void EntityManager::BurryEntities()
{
    //bring all alive entities to the front of the list
    std::partition( entityPool.begin( ), entityPool.end( ), not1( EntityDead( ) ) );

    //free all dead entities
    std::list< Entity* >::iterator begin( entityPool.begin( ) ), end( entityPool.end( ) );

    std::list< Entity* >::iterator firstDeadEntity = std::find_if( begin, end, EntityDead( ) );

    std::for_each( firstDeadEntity, end, FreePointer<Entity > ( ) );

    entityPool.erase( firstDeadEntity, end );
}

/**
 * Destroys all entities managed by the entity manager.
 */

void EntityManager::DestroyAllEntities()
{
    std::list<Entity*>::iterator begin( entityPool.begin( ) ), end( entityPool.end( ) );

    std::for_each( begin, end, FreePointer<Entity > ( ) );

    entityPool.clear( );
}

/**
 * Informs all entities about a keyboard event.
 *
 * @param event: an occurred event.
 */

void EntityManager::PushKeyboardInput(KeyboardEvent& event) const
{
    std::list< Entity* >::const_iterator iter( entityPool.begin( ) ), end( entityPool.end( ) );
    //get the mapper and fin the association for this event
    KeyMapper *mapper = ServiceLocator< KeyMapper >::GetService( );
    std::string association;
    mapper->MapKey( association, event.GetKey( ), event.GetModifiers( ) );

    for(; iter != end; ++iter )
    {
        ( *iter )->PushKeyboardInput( event, association );
    }
}

/**
 * Informs all entities about a mouse event.
 * 
 * @param event: an occurred mouse event.
 */

void EntityManager::PushMouseInput(MouseEvent& event) const
{
}

}
